
import { getCurrentInstance, toRefs, inject, watch, reactive, effectScope, computed, isRef, isReactive } from 'vue'
import { addSubscribition, triggersubscribtion } from "./subscribe"
import { activePinia } from "./createPinia"
const isfn = (value) => typeof value === "function"
const isObj = (value) => typeof value === "object" && value !== null
export function defineStore(idOrOptions, setup) {
  let id, objOrSetup
  if (typeof idOrOptions === "string") {
    id = idOrOptions
    objOrSetup = setup
  } else {
    id = idOrOptions.id
    objOrSetup = idOrOptions
  }
  function createSetupStore(id, objOrSetup, pinia) {
    let scope
    function margeObj(state = {}, objOrfn) {
      for (const key in objOrfn) {
        let oldVal = state[key]
        let newVal = objOrfn[key]
        if (isObj(oldVal) && isObj(newVal)) {
          state[key] = margeObj(oldVal, newVal)
        } else {
          state[key] = newVal
        }
      }
      return state
    }
    function $patch(objOrfn) {
      if (isfn(objOrfn)) {
        objOrfn(pinia.state.value[id])
      } else {
        margeObj(pinia.state.value[id], objOrfn)
      }
    }
    function $subscribe(callback, options = {}) {
      scope.run(() => watch(pinia.state.value[id], (state) => {
        callback({ storeId: id }, state)
      }, options))
    }
    let actionsubScribe = []
    const partialStore = {
      $patch,
      $subscribe,
      $onAction: addSubscribition.bind(null, actionsubScribe),
      $dispose() {
        scope.stop()  // 停止响应式(修改数据页面还是会更新的,但是停止了computed,watch,watchEffect的效果) 
        actionsubScribe = []// 停止订阅
        pinia._s.delete(id) // 
      }
    }
    const store = reactive(partialStore) //后续不是用户定义的属性和方法，内置的api添加到这个store上
    //外层scope可以停止自己和内部嵌套scope中的响应式效果
    const setupStore = pinia._e.run(() => {
      scope = effectScope()
      return scope.run(() => objOrSetup())
    })

    function wrapAction(value) {//对action做拦截
      return function (...args) {
        //监听actions中的方法
        const aftersubScribitions = []
        const errorsubScribitions = []
        function after(callback) {
          aftersubScribitions.push(callback)
        }
        function onError(callback) {
          errorsubScribitions.push(callback)
        }
        triggersubscribtion(actionsubScribe, { after, onError })
        let res
        try {
          res = value.apply(store, args)
        } catch (error) {
          triggersubscribtion(errorsubScribitions, error)
          throw error
        }
        if (res instanceof Promise) {
          return res.then((val) => {
            triggersubscribtion(aftersubScribitions, val)
            return val
          }).catch((error) => {
            triggersubscribtion(errorsubScribitions, error)
            return Promise.reject(error)
          })
        }
        triggersubscribtion(aftersubScribitions, res)
        return res
      }
    }
    function computedRef(value) {
      return !!isRef(value) && value.effect
    }
    //修改actions中的this指向store
    for (const key in setupStore) {
      const value = setupStore[key]
      if (isfn(value)) {
        setupStore[key] = wrapAction(value)
      }
      //如果objOrSetup是setup函数执行下面操作 把setup函数中ref和reactive,不能是computed的值保存到pinia.state中
      if (isRef(value) && !computedRef(value) || isReactive(value)) {
        if (!pinia.state.value[id]) {
          pinia.state.value[id] = {}
          pinia.state.value[id][key] = value
        }
      }
    }
    Object.assign(store, setupStore)
    pinia._s.set(id, store)
    console.log(pinia.state.value, store, "45622");
    //给store添加一个$state属性
    Object.defineProperty(store, '$state', {
      get: () => pinia.state.value[id],
      set: (state) => $patch($state => Object.assign($state, state))
    })
    store.$id = id
    store._p = pinia
    pinia._p.forEach(plugin => {
      Object.assign(store, scope.run(() => plugin({ store }))) //把plugin函数的的返回值合并到store中
    })
    return store
  }
  function createOptionsStore(id, objOrSetup, pinia) {
    const { state, getters = {}, actions = {} } = objOrSetup
    function setup() {
      pinia.state.value[id] = state ? state() : {}
      const localState = toRefs(pinia.state.value[id])
      return Object.assign(localState, actions, Object.keys(getters).reduce(function (pre, current) {
        //pinia._s.get(id)在我们访问计算属性值的时候已经有值了
        pre[current] = computed(() => getters[current].call(pinia._s.get(id)))
        return pre
      }, {}))
    }

    const store = createSetupStore(id, setup, pinia)
    //只有options有重置状态，组合式没有这个方法
    store.$reset = function () {
      const oldValue = state ? state() : {}
      store.$patch((state) => {
        Object.assign(state, oldValue)
      })
    }

  }
  function userStore() {
    // const instance = getCurrentInstance()
    // let pinia = instance && inject("_piniaSymbol_")
    // if (!pinia) pinia = activePinia  
    let pinia = activePinia  //在任何地方都能使用pinia
    if (!pinia._s.get(id)) {
      if (isfn(objOrSetup)) {
        createSetupStore(id, objOrSetup, pinia)
      } else {
        createOptionsStore(id, objOrSetup, pinia)
      }
    }
    const store = pinia._s.get(id)
    return store
  }
  return userStore
}